package datas

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"log"
	"net"
	"strings"
	"sync"

	"github.com/c-bata/go-prompt"
)

type G map[string]string
type Gi map[string]interface{}
type GD map[string]Gi
type GL map[string][]string
type Mem struct {
	Kv G
	Kd GD
	Kl GL
}

var (
	MemDB = &Mem{
		Kv: make(G),
		Kd: make(GD),
		Kl: make(GL),
	}
	HIST   = []string{}
	Locker = sync.Mutex{}
)

func (g G) Keys() (o []string) {
	for k := range g {
		o = append(o, k)
	}
	return
}
func (g Gi) Keys() (o []string) {
	for k := range g {
		o = append(o, k)
	}
	return
}

func (mem *Mem) Sync(con net.Conn) {
	defer con.Close()
	buf, err := ReadAll(con)
	if err != nil {
		log.Println("cli error:", err)
		con.Close()
		return
	}
	newmem := Mem{}
	if err := json.Unmarshal(buf, &newmem); err != nil {
		log.Println("cli error:", err)
		con.Close()
		return
	}
	for k, v := range newmem.Kd {
		if strings.HasPrefix(k, "c:") || strings.HasPrefix(k, "m:") {
			mem.Kd[k] = v
		}
	}
}

func (mem *Mem) ConfigCliServer(con net.Conn) {
	// man := ManWraper(con)
	// defer con.Close()
	buf, err := ReadAll(con)
	errdeal := func(err error) bool {
		if err != nil {
			log.Println("cli error:", err)
			con.Close()
			return true
		}
		return false
	}
	if errdeal(err) {
		return
	}
	if bytes.HasPrefix(buf, []byte("sugg:")) {
		data, err := json.Marshal(&MemDB)
		if errdeal(err) {
			return
		}
		WriteALl(con, data)
	}
	for {
		msgbuf, err := ReadAll(con)
		if errdeal(err) {
			return
		}
		msg := string(msgbuf)
		repl, code := mem.Do(msg)
		log.Println(string(repl), code)

		if code < 0 {
			log.Println("Err Msg:", repl)
		}
		if repl == nil {
			log.Println("exit config cli")
			break
		}
		WriteALl(con, repl)
	}

}

func (mem *Mem) Cli(con net.Conn) {
	WriteALl(con, []byte("sugg:"))
	memData, err := ReadAll(con)
	if err != nil {
		log.Fatal("read init data:", err)
	}
	err = json.Unmarshal(memData, &MemDB)
	if err != nil {
		log.Fatal("init for local mem err:", err)
	}
	completer := func(d prompt.Document) (s []prompt.Suggest) {
		fiedls := strings.Fields(d.TextBeforeCursor())
		s = append(s, prompt.Suggest{Text: "load local json file"})
		if len(fiedls) > 0 {
			switch fiedls[0] {
			case "get", "set", "del":
				for k := range mem.Kv {
					s = append(s, prompt.Suggest{Text: k})
				}
			case "lget", "lset", "ldel":

				for k := range mem.Kl {
					s = append(s, prompt.Suggest{Text: k})
				}

			case "dget", "dset", "ddel":

				for k := range mem.Kd {
					s = append(s, prompt.Suggest{Text: k})
				}
			default:
				for _, k := range []string{"clear", "exit", "get", "ls", "set", "del", "lget", "lset", "ldel", "dget", "dset", "ddel"} {
					s = append(s, prompt.Suggest{Text: k})
				}
			}
		} else {
			for _, k := range []string{"clear", "exit", "get", "ls", "set", "del", "lget", "lset", "ldel", "dget", "dset", "ddel"} {
				s = append(s, prompt.Suggest{Text: k})
			}
		}
		return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true)
	}
	for {

		result := prompt.Input("Connected Remote Config >", completer, prompt.OptionHistory(HIST))
		if strings.Contains(result, "dset") {
			fs := strings.Fields(result)
			dict := JsonCreateEasy()
			if len(dict) == 0 {
				continue
			}
			dd, err := json.Marshal(&dict)
			if err != nil {
				log.Println("dset json err:", err)
				continue
			}
			if len(fs) == 1 {
				key := prompt.Input("key name :", func(d prompt.Document) (s []prompt.Suggest) {
					return
				})
				if key == "" {
					continue
				}
				result = fmt.Sprintf("%s %s %s", fs[0], key, base64.StdEncoding.EncodeToString(dd))
			} else {
				result = fmt.Sprintf("%s %s %s", fs[0], fs[1], base64.StdEncoding.EncodeToString(dd))
			}

		}
		WriteALl(con, []byte(result))
		if result == "exit" {
			break
		}
		repl, err := ReadAll(con)
		if err != nil {
			log.Println("read reply err:", err)
			continue
		}
		fmt.Println(string(repl))
		mem.Do(result)
		HIST = append(HIST, result)
	}
}

func (mem *Mem) Do(msg string) (data []byte, n int) {
	buffer := bytes.NewBuffer([]byte{})
	filds := strings.Fields(msg)
	if len(filds) == 0 {
		return []byte("no enought args"), -1
	}
	if filds[0] == "exit" {
		return nil, 0
	}
	switch filds[0] {
	case "clear":
		mem.Kv = make(G)
		mem.Kd = make(GD)
		mem.Kl = make(GL)
		buffer.WriteString("clearall")
		return buffer.Bytes(), 0
	case "ls":

		buffer.WriteString("Kv ----\n")
		for k := range mem.Kv {
			buffer.WriteString("\t" + k + "\n")
		}

		buffer.WriteString("Kl ----\n")
		for k := range mem.Kl {
			buffer.WriteString("\t" + k + "\n")
		}

		buffer.WriteString("Kd ----\n")
		for k := range mem.Kd {
			buffer.WriteString("\t" + k + "\n")
		}
		return buffer.Bytes(), 0
	}
	if len(filds) < 2 {
		return []byte("no enought args"), -2
	}
	switch filds[0] {
	case "get":
		if m, ok := mem.Kv[filds[1]]; ok {
			buffer.WriteString(m)
		} else {
			buffer.WriteString("not found!" + filds[1])
		}

	case "lget":
		data, err := json.Marshal(mem.Kl[filds[1]])
		if err != nil {
			buffer.WriteString(err.Error())
		} else {

			buffer.Write(data)
		}
	case "dget":
		data, err := json.Marshal(mem.Kd[filds[1]])
		if err != nil {
			buffer.WriteString(err.Error())
		} else {
			buffer.Write(data)
		}
	case "set":
		Locker.Lock()
		defer Locker.Unlock()
		if len(filds) < 3 {
			return []byte("no enought args"), -3
		}
		buffer.WriteString("set " + filds[1] + " => " + filds[2])
		mem.Kv[filds[1]] = filds[2]

	case "lset":
		Locker.Lock()
		defer Locker.Unlock()
		if len(filds) < 3 {
			return []byte("no enought args"), -3
		}
		mp := []string{}
		value, err := base64.StdEncoding.DecodeString(filds[2])
		if err != nil {
			buffer.WriteString(err.Error())
			return buffer.Bytes(), -1
		}
		err = json.Unmarshal(value, &mp)
		if err != nil {
			buffer.WriteString(err.Error())
		} else {
			buffer.WriteString("set " + filds[1])
		}
		mem.Kl[filds[1]] = mp

	case "dset":
		Locker.Lock()
		defer Locker.Unlock()
		if len(filds) < 3 {
			return []byte("no enought args"), -3
		}
		mp := make(Gi)
		value, err := base64.StdEncoding.DecodeString(filds[2])
		if err != nil {
			buffer.WriteString(err.Error())
			return buffer.Bytes(), -1
		}
		err = json.Unmarshal(value, &mp)
		if err != nil {
			buffer.WriteString(err.Error())
		} else {
			buffer.WriteString("set " + filds[1])
		}
		mem.Kd[filds[1]] = mp

	case "ldel", "del", "ddel":
		Locker.Lock()
		defer Locker.Unlock()
		if filds[0][0] == 'd' {
			delete(mem.Kd, filds[1])
		} else if filds[0][0] == 'l' {
			delete(mem.Kl, filds[1])
		} else {
			delete(mem.Kv, filds[1])
		}
		buffer.WriteString("del " + filds[1])
	}
	return buffer.Bytes(), 0
}

func NewMemDBCli(conn net.Conn) {
	defer conn.Close()
	if _, err := conn.Write([]byte("::config::")); err != nil {
		log.Fatal("::config:: ewrr :", err)
	}
	buf, err := ReadAll(conn)
	if err != nil {
		log.Fatal("inti err:", err)
	}
	if string(buf) != "ok" {
		log.Fatal(string(buf))
	} else {
		log.Println("init ok ")
	}
	MemDB.Cli(conn)
}

func SyncCli(conn net.Conn) {
	if data, err := json.Marshal(&MemDB); err != nil {
		log.Println("jsonize failed: ", err)
	} else {
		if _, err := WriteALl(conn, data); err != nil {
			log.Println("json data send failed:", err)
		} else {
			if buf, err := ReadAll(conn); err != nil {
				log.Println("get result :", err)
			} else {
				log.Println(string(buf))
			}
		}
	}

}

func MemDBDel(key string) {
	_, ok := MemDB.Kd[key]
	if ok {
		Locker.Lock()
		defer Locker.Unlock()
		delete(MemDB.Kd, key)
	}
}

func MemDBSave(key string, jsonData []byte) {
	data := make(map[string]interface{})
	if err := json.Unmarshal(jsonData, &data); err != nil {
		log.Println(err)
		return
	}
	Locker.Lock()
	defer Locker.Unlock()
	MemDB.Kd[key] = data
}

func MemDBQuery(key string) string {
	data, ok := MemDB.Kd[key]
	if !ok {
		return ""
	}
	if buf, err := json.MarshalIndent(data, "", "\t"); err != nil {
		log.Println(err)
		return ""
	} else {
		return string(buf)
	}
}

var UploadFileManager = struct {
	UploadedFile func(raw, uname string)
	Query        func(name string, reverse ...bool) (string, bool)
}{
	UploadedFile: func(raw, uname string) {
		Locker.Lock()
		defer Locker.Unlock()
		old, ok := MemDB.Kd["Uploaded"]
		if !ok {
			old = Gi{}
		}
		oldR, ok2 := MemDB.Kd["UploadedR"]
		if !ok2 {
			oldR = Gi{}
		}
		oldR[raw] = uname
		old[uname] = raw
		MemDB.Kd["Uploaded"] = old
		MemDB.Kd["UploadedR"] = oldR
	},
	Query: func(name string, reverse ...bool) (string, bool) {
		if reverse != nil {
			if o, ok := MemDB.Kd["UploadedR"]; ok {
				a, ok := o[name]
				return a.(string), ok
			}
		}
		if o, ok := MemDB.Kd["Uploaded"]; ok {
			a, ok := o[name]
			return a.(string), ok
		}
		return "", false
	},
}

func MemDBSaferWith(oper func(mem *Mem)) {
	Locker.Lock()
	defer Locker.Unlock()
	oper(MemDB)
}
